package com.xjob.util;


import cn.hutool.json.JSONUtil;
import com.xjob.enums.MessageRemindFrequencyType;
import com.xjob.exception.ServiceException;
import com.xjob.model.dto.MessageRemindDto;
import com.xjob.model.dto.XxlJobInfoDto;
import com.xjob.model.query.XxlJobQuery;
import com.xxl.job.core.biz.model.ReturnT;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.http.*;
import org.springframework.stereotype.Component;
import org.springframework.util.LinkedMultiValueMap;
import org.springframework.util.MultiValueMap;
import org.springframework.web.client.RestTemplate;

import javax.annotation.Resource;
import java.text.SimpleDateFormat;
import java.util.*;

/**
 * @description: 手动添加任务
 * @author: xianggaoxiong
 * @create: 2020-05-20 14:13
 **/
@Component
@Slf4j
public class XxlJobUtil {
    @Value("${xxl-job.admin.admin-addresses}")
    private String addresses;
    @Value("${xxl-job.loginName}")
    private String userName;
    @Value("${xxl-job.password}")
    private String password;
    @Value("${xxl-job.group}")
    private String jobGroup;
    @Resource
    private RestTemplate restTemplate;

    /**
     * 定时任务添加
     *
     * @param jobInfo
     * @return
     * @throws ServiceException
     */
    public String add(XxlJobInfoDto jobInfo) throws ServiceException {
        String url = getAddresses() + "/jobinfo/add";
        HttpEntity<MultiValueMap<String, String>> request = getMultiValueMapHttpEntity(jobInfo);
        ResponseEntity<ReturnT> response = restTemplate.postForEntity(url, request, ReturnT.class);
        if (!HttpStatus.OK.equals(response.getStatusCode())) {
            throw new ServiceException("【xjob定时任务】保存请求失败！！！");
        } else {
            log.info("【xjob定时任务】保存请求返回:{}", response.getBody());
            if (response.getBody().getCode() == HttpStatus.OK.value()) {
                return String.valueOf(response.getBody().getContent());
            } else {
                throw new ServiceException("【xjob定时任务】内部保存异常：" + response.getBody().getMsg());
            }
        }
    }

    /**
     * 定时任务更新
     *
     * @param jobInfo
     * @throws ServiceException
     */
    public void update(XxlJobInfoDto jobInfo) throws ServiceException {
        String url = getAddresses() + "/jobinfo/update";
        HttpEntity<MultiValueMap<String, String>> request = getMultiValueMapHttpEntity(jobInfo);
        ResponseEntity<ReturnT> response = restTemplate.postForEntity(url, request, ReturnT.class);
        if (!HttpStatus.OK.equals(response.getStatusCode())) {
            throw new ServiceException("【xjob定时任务】更新请求失败！！！");
        } else {
            log.info("【xjob定时任务】更新请求返回:{}", response.getBody());
            if (response.getBody().getCode() != HttpStatus.OK.value()) {
                throw new ServiceException("【xjob定时任务】内部更新异常：" + response.getBody().getMsg());
            }
        }
    }

    private HttpEntity<MultiValueMap<String, String>> getMultiValueMapHttpEntity(XxlJobInfoDto jobInfo) {
        Map<String, String> parameters = new HashMap<>(16);
        // 执行器主键ID
        parameters.put("id", String.valueOf(jobInfo.getId()));
        // 执行器主键ID 在xjob管理界面添加执行器后 执行器的id
        parameters.put("jobGroup", jobGroup);
        // 任务执行CRON表达式
        parameters.put("jobCron", jobInfo.getJobCron());
        // 任务描述
        parameters.put("jobDesc", jobInfo.getJobDesc());
        //负责人
        parameters.put("author", jobInfo.getAuthor());
        // 报警邮件
        parameters.put("alarmEmail", jobInfo.getAlarmEmail());
        //运行模式
        parameters.put("glueType", jobInfo.getGlueType());
        // 执行器路由策略
        parameters.put("executorRouteStrategy", jobInfo.getExecutorRouteStrategy());
        // 执行器，任务Handler名称
        parameters.put("executorHandler", jobInfo.getExecutorHandler());
        // 执行器，任务参数
        parameters.put("executorParam", jobInfo.getExecutorParam());
        // 阻塞处理策略
        parameters.put("executorBlockStrategy", jobInfo.getExecutorBlockStrategy());
        //任务执行超时时间，单位秒
        parameters.put("executorTimeout", String.valueOf(jobInfo.getExecutorTimeout()));
        // 失败重试次数
        parameters.put("executorFailRetryCount", String.valueOf(jobInfo.getExecutorFailRetryCount()));
        return getMultiValueMapHttpEntity(parameters);
    }


    /**
     * 定时任务删除
     *
     * @param id
     * @throws ServiceException
     */
    public void remove(int id) throws ServiceException {
        Map<String, String> map = new HashMap<>(1);
        map.put("id", String.valueOf(id));
        log.info("【xjob定时任务】删除请求参数:{}", JSONUtil.toJsonStr(map));
        HttpEntity<MultiValueMap<String, String>> request = getMultiValueMapHttpEntity(map);
        String url = getAddresses() + "/jobinfo/remove";
        ResponseEntity<ReturnT> response = restTemplate.postForEntity(url, request, ReturnT.class);
        if (!HttpStatus.OK.equals(response.getStatusCode())) {
            throw new ServiceException("【xjob定时任务】删除请求失败！！！");
        } else {
            log.info("【xjob定时任务】删除请求返回:{}", response.getBody());
            if (response.getBody().getCode() != HttpStatus.OK.value()) {
                throw new ServiceException("【xjob定时任务】内部删除异常：" + response.getBody().getMsg());
            }
        }
    }


    /**
     * 定时任务暂停
     *
     * @param id
     * @throws ServiceException
     */
    public void pause(int id) throws ServiceException {
        Map<String, String> map = new HashMap<>(1);
        map.put("id", String.valueOf(id));
        HttpEntity<MultiValueMap<String, String>> request = getMultiValueMapHttpEntity(map);
        String url = getAddresses() + "/jobinfo/stop";
        ResponseEntity<ReturnT> response = restTemplate.postForEntity(url, request, ReturnT.class);
        if (!HttpStatus.OK.equals(response.getStatusCode())) {
            throw new ServiceException("【xjob定时任务】暂停请求失败！！！");
        } else {
            log.info("【xjob定时任务】暂停请求返回:{}", response.getBody());
            if (response.getBody().getCode() != HttpStatus.OK.value()) {
                throw new ServiceException("【xjob定时任务】内部暂停异常：" + response.getBody().getMsg());
            }
        }
    }


    /**
     * 定时任务启动
     *
     * @param id
     * @throws ServiceException
     */
    public void start(int id) throws ServiceException {
        Map<String, String> map = new HashMap<>(1);
        map.put("id", String.valueOf(id));
        HttpEntity<MultiValueMap<String, String>> request = getMultiValueMapHttpEntity(map);
        String url = getAddresses() + "/jobinfo/start";
        ResponseEntity<ReturnT> response = restTemplate.postForEntity(url, request, ReturnT.class);
        if (!HttpStatus.OK.equals(response.getStatusCode())) {
            throw new ServiceException("【xjob定时任务】启动请求失败！！！");
        } else {
            log.info("【xjob定时任务】启动请求返回:{}", response.getBody());
            if (response.getBody().getCode() != HttpStatus.OK.value()) {
                throw new ServiceException("【xjob定时任务】内部启动异常：" + response.getBody().getMsg());
            }
        }
    }


    /**
     * 获取列表
     *
     * @param query
     * @return
     * @throws ServiceException
     */
    public Map<String, Object> pageList(XxlJobQuery query) throws ServiceException {
        Map<String, String> map = new HashMap<>(8);
        map.put("jobGroup", String.valueOf(query.getJobGroup()));
        map.put("triggerStatus", String.valueOf(query.getTriggerStatus()));
        map.put("jobDesc", query.getJobDesc());
        map.put("executorHandler", query.getExecutorHandler());
        map.put("author", query.getAuthor());
        HttpEntity<MultiValueMap<String, String>> request = getMultiValueMapHttpEntity(map);
        String url = getAddresses() + "/jobinfo/pageList?start=" + query.getPageNum() + "&length=" + query.getPageSize();
        ResponseEntity<Map> response = restTemplate.postForEntity(url, request, Map.class);
        if (!HttpStatus.OK.equals(response.getStatusCode())) {
            throw new ServiceException("【xjob定时任务】列表查询请求失败！！！");
        } else {
            log.info("【xjob定时任务】列表查询请求返回:{}", response.getBody());
            return (Map<String, Object>) response.getBody();
        }
    }

    private HttpEntity<MultiValueMap<String, String>> getMultiValueMapHttpEntity(Map<String, String> params) {
        HttpHeaders headers = new HttpHeaders();
        String cookie = this.login();
        List<String> cookies = new ArrayList<String>();
        cookies.add(cookie);
        headers.put(HttpHeaders.COOKIE, cookies);
        headers.setContentType(MediaType.APPLICATION_FORM_URLENCODED);
        MultiValueMap<String, String> parameters = new LinkedMultiValueMap<String, String>();
        parameters.setAll(params);
        HttpEntity<MultiValueMap<String, String>> request = new HttpEntity<>(parameters, headers);
        return request;
    }


    public String login() {
        String url = getAddresses() + "/login";
        HttpHeaders headers = new HttpHeaders();
        headers.setContentType(MediaType.APPLICATION_FORM_URLENCODED);
        //请求参数
        MultiValueMap<String, String> params = new LinkedMultiValueMap<String, String>();
        params.add("userName", userName);
        params.add("password", password);
        HttpEntity<MultiValueMap<String, String>> request = new HttpEntity<MultiValueMap<String, String>>(params, headers);
        ResponseEntity<String> response = restTemplate.postForEntity(url, request, String.class);
        String setCookies = response.getHeaders().get(HttpHeaders.SET_COOKIE).get(0);
        log.info("【xjob定时任务】登入获取cookie：{}", setCookies);
        return setCookies;
    }

    private String getAddresses() {
        String[] split = addresses.split(",");
        Random rand = new Random();
        int index = rand.nextInt(split.length);
        return split[index];
    }

    /**
     * 根据规则获取cron表达式
     */
    private static String getCron(MessageRemindDto messageRemindReq) {
        String cron = null;
        if (messageRemindReq.getSendFrequency() == MessageRemindFrequencyType.FREQUENCY_ONCE.getCode()) {
            String dateFormat = "ss mm HH dd MM ? yyyy";
            SimpleDateFormat sdf = new SimpleDateFormat(dateFormat);
            cron = sdf.format(messageRemindReq.getStartTime());
        } else if (messageRemindReq.getSendFrequency() == MessageRemindFrequencyType.FREQUENCY_DAY.getCode()) {
            String pollHourMin = messageRemindReq.getPollHourMin();
            String[] split = pollHourMin.split(":");
            String min = split[1];
            String hour = split[0];
            if (messageRemindReq.getIsWeek() == 1) {
                StringBuilder sb = new StringBuilder();
                sb.append("0 ").append(min + " ").append(hour + " ? * *");
                cron = sb.toString();
            }
            if (messageRemindReq.getIsWeek() == 0) {
                StringBuilder sb = new StringBuilder();
                sb.append("0 ").append(min + " ").append(hour + " ? * MON-FRI");
                cron = sb.toString();
            }
        } else if (messageRemindReq.getSendFrequency() == MessageRemindFrequencyType.FREQUENCY_WEEK.getCode()) {
            String pollHourMin = messageRemindReq.getPollHourMin();
            String[] split = pollHourMin.split(":");
            String min = split[1];
            String hour = split[0];
            StringBuilder sb = new StringBuilder();
            sb.append("0 ").append(min + " ").append(hour + " ").append("? * ").append(messageRemindReq.getPollWeekday());
            cron = sb.toString();
        } else if (messageRemindReq.getSendFrequency() == MessageRemindFrequencyType.FREQUENCY_MONTH.getCode()) {
            String pollHourMin = messageRemindReq.getPollHourMin();
            String[] split = pollHourMin.split(":");
            String min = split[1];
            String hour = split[0];
            StringBuilder sb = new StringBuilder();
            //0 0 12 1 1/3 ? 从1月开始每隔三个月(1,4,7,10)的1号12点执行
            sb.append("0 ").append(min + " ").append(hour + " ").append(messageRemindReq.getPollMonthday() + " 1/").append(messageRemindReq.getPollMonth() + " ?");
            cron = sb.toString();
        } else if (messageRemindReq.getSendFrequency() == MessageRemindFrequencyType.FREQUENCY_IN_TIME.getCode()) {
            cron = "";
        }
        return cron;
    }
}
